#!/bin/bash

################################# Functions ####################################

shopt -s expand_aliases
alias log="log_message "\$FUNCNAME:\$LINENO""
alias exec_cmd="execute_command "\$FUNCNAME:\$LINENO""
COMMAND=${COMMAND:-"$(basename "$0")"}

function print_help {
    echo 'Synchronize repositories - export all changes from ClearCase to Subversion.'
    echo
    print_usage
}

function print_usage {
    echo "Usage: $COMMAND [svn dir] [options]"
    echo '    [svn dir]  : Path to a directory at which the Subversion repository is located.'
    echo 
    echo 'Options:'
    echo '    -u         : Update ClearCase repository bofore synchronization.'
    echo '    -p         : A path, relative to $SVN_REPO_ROOT, to be synchronized'
    echo '    -c         : Cleanup before synchronization.'
}

function cc_update {
    log "Updating ClearCase view: ${SYNC_DIR}"

    if [ ! -z "$IS_CYGWIN" ]
    then
        exec_cmd "$CLEARTOOL_CMD" update -f -overwrite "$(cygpath -am ${SYNC_DIR})" &>> "$LOG_FILE"
    else
        exec_cmd "$CLEARTOOL_CMD" update -f -overwrite "${SYNC_DIR}" &>> "$LOG_FILE"
    fi
    
    [[  $EXIT_CODE != 0 ]] && echo_err "Failed to update ClearCase view" && fail $EXIT_CODE
}

function svn_lock {
    log "Locking repository: $SVN_REPO_DIR"
    exec_cmd mkdir "$SYNC_LOCK"
    [[  $EXIT_CODE != 0 ]] && echo_err "Failed to lock repository: $SVN_REPO_URL" && fail $EXIT_CODE
}

function svn_unlock {
    log "Unlocking repository: $SVN_REPO_DIR"
    exec_cmd rmdir "$SYNC_LOCK"
}

function svn_update {
    log "Updating working copy: $SYNC_DIR"
    exec_cmd "$SVN_CMD" "${SVN_ARGS[@]}" up --force --accept mine-full "$SYNC_DIR" >> "$LOG_FILE"
    [[  $EXIT_CODE != 0 ]] && echo_err "Failed to update working copy: $SYNC_DIR" && fail $EXIT_CODE
}

function rollback_and_fail {
    log "Error occurred. Rolling back changes."
    local ec="$EXIT_CODE"
    exec_cmd "$SVN_CMD" "${SVN_ARGS[@]}" revert -R "$SYNC_DIR" >> "$LOG_FILE"
    svn_unlock
    fail $ec
}

function should_ignore {
       local i=0
       local size=${#SYNC_IGNORE[@]}
       
       for ((i = 0; i < size; i++))
       do
           [[ $1 == ${SYNC_IGNORE[$i]} ]] && return 0
       done
       
       return 1
}

function copy_resource {
    should_ignore "$1"&& log "Ignoring: $i" && return 0

    if [ -f "$1" ] 
    then
          exec_cmd cp "$1" "$2"
          [[  $EXIT_CODE != 0 ]] && log "Failed to copy $1 > $2" && rollback_and_fail
    else
        mkdir "$2"
        local i
        local f
        
        for i in "$1"/*
        do
            [[ "$i" == *'*' ]] && continue
            f="$2/${i##*/}"
            copy_resource "$i" "$f"
        done
    fi
}

function svn_rm {
    exec_cmd "$SVN_CMD" "${SVN_ARGS[@]}" rm "$1" >> "$LOG_FILE"
    [[  $EXIT_CODE != 0 ]] && rollback_and_fail
    HAS_CHANGES="true"
}

function cc_describe {
    local desc=`exec_cmd "$CLEARTOOL_CMD" describe -fmt "$SYNC_CC_DESC_FORMAT" "$1"`
    [[  $EXIT_CODE != 0 ]] && rollback_and_fail
    SYNC_LOG_MESSAGE=`echo -e "${SYNC_LOG_MESSAGE}${NEW_LINE}${desc}"`
}

function svn_add {
    exec_cmd "$SVN_CMD" "${SVN_ARGS[@]}" add "$1" >> "$LOG_FILE"
    [[  $EXIT_CODE != 0 ]] && rollback_and_fail
    cc_describe "$1"
    HAS_CHANGES="true"
}

function svn_ci {
    log "Committing changes to: $SVN_REPO_URL"
    exec_cmd "$SVN_CMD" "${SVN_ARGS[@]}" ci "$SYNC_DIR" -m "$SYNC_LOG_MESSAGE" --username "$SYNC_USERNAME" >> "$LOG_FILE"
    [[  $EXIT_CODE != 0 ]] && echo_err "Failed to committing changes to $SVN_REPO_URL" && fail $EXIT_CODE
}

function svncc_sync {
    log "Synchronizing $SYNC_DIR"
    
    local status=$(exec_cmd "$SVN_CMD" "${SVN_ARGS[@]}" status)
    [[  $EXIT_CODE != 0 ]] && rollback_and_fail
    echo "$status" >> "$LOG_FILE"
    
    ORIG_IFS=$IFS
    IFS=$'\n'
    for line in $status
    do
        op=${line:0:1}
        f=$(trim ${line:1})
        
        case "$op" in
            "M") cc_describe "$f"; HAS_CHANGES="true" ;;
            "?") svn_add "$f" ;;
            "!") svn_rm "$f" ;;
            "~")
                 exec_cmd rm -rf "$f"
                 svn_rm "$f"
                 [[  $EXIT_CODE != 0 ]] && rollback_and_fail
                 exec_cmd "$CLEARTOOL_CMD" update -f "$f"
                 svn_add "$f"
                 [[  $EXIT_CODE != 0 ]] && rollback_and_fail
                 HAS_CHANGES="true"
            ;;
    esac
    done
    IFS=$ORIG_IFS
}

################################################################################

if [[ $1 == help ]]
then
      print_help
      exit 0  
fi

SCRIPT_PATH=${SCRIPT_PATH:-"$0"}
SCRIPT_DIR=`dirname "$SCRIPT_PATH"`

if [[ -z $1 || $1 == -* ]] 
then
    SVN_REPO_DIR="$PWD"
else
    SVN_REPO_DIR="$1"
    shift
fi

[ ! -d "$SVN_REPO_DIR/hooks" ] && echo "Subversion repository not found at $SVN_REPO_DIR" 1>&2 && exit 1

for i in "$@"
do
    case "$i" in
        "-u") CC_UPDATE="true"; unset IS_PATH ;;
        "-p") IS_PATH="true" ;;
        "-c") CLEANUP="true"; unset IS_PATH ;;
         *) 
             if [ ! -z "$IS_PATH" ]
             then
                 REPO_PATH="/${i//\\//}"
                 unset IS_PATH
             else
                 echo_err "Invalid option: $i"
                 print_usage
                 exit 1
             fi
             ;;
         esac
done

SVN_REPO_CFG="$SVN_REPO_DIR/svncc/svncc.cfg"
[ ! -f "$SVN_REPO_CFG" ] && echo "svncc configuration file not found at $SVN_REPO_CFG" 1>&2 && exit 1
START_TIME=`date +%s`
LOG_FILE_SUFFIX="_$(date +%d%m%y_%H%M).log"

. "$SVN_REPO_CFG"
. "$SCRIPT_DIR/common.sh"

if [ ! -z "$IS_CYGWIN" ]
then
    SVN_REPO_DIR=$(cd "$SVN_REPO_DIR"; pwd)
    CC_VIEW_DIR=$(cd "$CC_VIEW_DIR"; pwd)
else
    IS_CYGWIN="true"
    SVN_REPO_DIR=$(cygpath -a "$SVN_REPO_DIR")
    CC_VIEW_DIR=$(cygpath -am "$CC_VIEW_DIR")
fi

DELETE_LOG_ON_SUCCESS="$DELETE_SYNC_LOG_ON_SUCCESS"
DELETE_LOG_ON_FAILURE="$DELETE_SYNC_LOG_ON_FAILURE"
SVN_REPO_URL="file://$SVN_REPO_DIR/$SVN_REPO_ROOT"
SYNC_DIR="${CC_VIEW_DIR}${REPO_PATH}"

log "`date`| Synchronizing '${SYNC_DIR}' and '${SVN_REPO_URL}'"
SYNC_LOCK="$SVN_REPO_DIR/svncc/sync.lock"
svn_lock

cd "$SYNC_DIR"

if [ ! -z "$CLEANUP" ]
then
     log "Cleaning up $SYNC_DIR"
     
     for i in *
     do
         [[ "$i" != ".svn" && "$i" != "view.dat" ]] && exec_cmd rm -rf "$i"
     done
fi

svn_update
cc_update
svncc_sync

[ ! -z "$HAS_CHANGES" ] && svn_ci
END_TIME=`date +%s`
log "`date`| Synchronization completed. Total time: $(($END_TIME - $START_TIME)) seconds."
svn_unlock

